/**
 * Multi-client line-buffered TCP server. Provides a Listen method that accepts
 * a handler method taking a Client (both exported here).
 *
 * Sam Thorogood (c) 2010
 */

package main

import (
  "bufio"
  "fmt"
  "net"
  "os"
  "strings"
)

func Listen(target string, handler func(client Client)) (err os.Error) {
  // resolve listen host/port
  addr, err := net.ResolveTCPAddr(target)
  if err != nil {
    return
  }

  // listen on tcp
  listener, err := net.ListenTCP("tcp", addr)
  if err != nil {
    return
  }

  // accept and go handle clients
  for {
    conn, err := listener.AcceptTCP()
    if err != nil {
      listener.Close()
      return
    }

    go func() {
      fmt.Fprintln(os.Stderr, "client from", conn.RemoteAddr(), "->", conn.LocalAddr())
      s := &_Client{conn, bufio.NewReader(conn), false}
      defer s.close()
      handler(*s)
    }()
  }
  panic("should not get here")
}

type Client interface {
  Read() (s *string)
  Write(s string) (ok bool)
  Ok() (ok bool)
}

type _Client struct {
  conn *net.TCPConn
  reader *bufio.Reader
  closed bool
}

func (c _Client) Read() (ret_line *string) {
  line, err := c.reader.ReadString('\n')
  if err != nil {
    fmt.Fprintln(os.Stderr, "closing client; err reading:", err, "from", c.conn.RemoteAddr())
    c.conn.Close()
    c.closed = true
    ret_line = nil
    // TODO: return the last line anyway, cache err
  } else {
    // TODO: faster
    line = line[0:strings.LastIndex(line, "\n")]
    ret_line = &line
  }
  return
}

func (c _Client) Write(line string) (ok bool) {
  _, err := fmt.Fprintln(c.conn, line)
  if err != nil {
    fmt.Fprintln(os.Stderr, "closing client; err writing:", err, "to", c.conn.RemoteAddr())
    c.conn.Close()
    c.closed = true
    ok = false
  } else {
    ok = true
  }
  return
}

func (c _Client) Ok() (ok bool) {
  return !c.closed
}

/** Internal close method. */
func (c _Client) close() {
  c.conn.Close()
  c.closed = true
}
